package com.googlecode.connectlet.tsa;

import java.io.IOException;
import java.math.BigInteger;
import java.util.Date;
import java.util.Random;

import sun.security.pkcs.ContentInfo;
import sun.security.pkcs.PKCS7;
import sun.security.util.DerInputStream;
import sun.security.util.DerOutputStream;
import sun.security.util.DerValue;
import sun.security.util.ObjectIdentifier;

public class TstInfo {
	private static final String CUSTOM_TSA_POLICY_OID = "1.3.6.1.4.1.8341.0";

	private static Random random = new Random();

	private MessageImprint messageImprint;
	private Date timeStamp;
	private BigInteger serialNumber;
	private ObjectIdentifier policy;

	public void setSerialNumber(BigInteger serialNumber) {
		if (serialNumber == null) {
			byte[] snBytes = new byte[16];
			random.nextBytes(snBytes);
			snBytes[0] &= 0x7f;
			this.serialNumber = new BigInteger(snBytes);
		} else {
			this.serialNumber = serialNumber;
		}
	}

	public void setPolicy(ObjectIdentifier policy) {
		if (policy == null) {
			try {
				this.policy = new ObjectIdentifier(CUSTOM_TSA_POLICY_OID);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		} else {
			this.policy = policy;
		}
	}

	public TstInfo(MessageImprint messageImprint, Date timeStamp) {
		this.messageImprint = messageImprint;
		this.timeStamp = timeStamp;
		setPolicy(null);
		setSerialNumber(null);
	}

	public TstInfo(ObjectIdentifier algorithm, byte[] digest, Date timeStamp) {
		messageImprint = new MessageImprint(algorithm, digest);
		this.timeStamp = timeStamp;
		setPolicy(null);
		setSerialNumber(null);
	}

	public TstInfo(byte[] encoded) throws IOException {
		DerValue[] ders = new DerInputStream(encoded).getSequence(0);
		if (ders.length < 5) {
			throw new IOException("Invalid DER Format");
		}
		policy = ders[1].getOID();
		messageImprint = new MessageImprint(ders[2].toByteArray());
		serialNumber = ders[3].getBigInteger();
		timeStamp = ders[4].getGeneralizedTime();
	}

	public TstInfo(ContentInfo contentInfo) throws IOException {
		this(contentInfo.getData());
	}

	public TstInfo(PKCS7 token) throws IOException {
		this(token.getContentInfo());
	}

	public TstInfo(TimeStampResp tsResp) throws IOException {
		this(tsResp.getToken());
	}

	public MessageImprint getMessageImprint() {
		return messageImprint;
	}

	public ObjectIdentifier getAlgorithm() {
		return messageImprint.getAlgorithm();
	}

	public byte[] getDigest() {
		return messageImprint.getDigest();
	}

	public ObjectIdentifier getPolicy() {
		return policy;
	}

	public BigInteger getSerialNumber() {
		return serialNumber;
	}

	public Date getTimeStamp() {
		return timeStamp;
	}

	public byte[] getEncoded() throws IOException {
		DerOutputStream tstInfo = new DerOutputStream();
		tstInfo.putInteger(1);
		tstInfo.putOID(policy);
		tstInfo.write(messageImprint.getEncoded());
		tstInfo.putInteger(serialNumber);
		tstInfo.putGeneralizedTime(new Date());
		DerOutputStream dos = new DerOutputStream();
		dos.write(DerValue.tag_Sequence, tstInfo);
		return dos.toByteArray();
	}
}